/***
 * Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package br.com.caelum.vraptor.http.ognl;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Map;

import net.vidageek.mirror.dsl.Mirror;
import ognl.ArrayPropertyAccessor;
import ognl.OgnlContext;
import ognl.OgnlException;
import br.com.caelum.vraptor.proxy.Proxifier;
import br.com.caelum.vraptor.util.StringUtils;

/**
 * Deals with acessing values within an array.<br>
 * Re-instantiates and invokes setter methods on arrays as soon as they are not
 * big enough for all required parameters.
 *
 * @author Guilherme Silveira
 */
public class ArrayAccessor extends ArrayPropertyAccessor {

	@Override
	public Object getProperty(Map context, Object target, Object object) throws OgnlException {
	try {
		return super.getProperty(context, target, object);
	} catch (IndexOutOfBoundsException ex) {
		return null;
	}
	}

	@Override
	public void setProperty(Map context, Object array, Object key, Object value) throws OgnlException {

	int index = (Integer) key;
	int length = Array.getLength(array);
	if (length <= index) {
		Object newArray = copyOf(array, index, length);
		OgnlContext ctx = (OgnlContext) context;
		if (array == ctx.getRoot()) {
			ctx.setRoot(newArray);
		} else {
			String fieldName = ctx.getCurrentEvaluation().getPrevious().getNode().toString();
			Object origin = ctx.getCurrentEvaluation().getPrevious().getSource();
			
			Proxifier proxifier = (Proxifier) context.get("proxifier");
			Method setter = new ReflectionBasedNullHandler(proxifier).findMethod(origin.getClass(),
				"set" + StringUtils.capitalize(fieldName), origin.getClass(), null);
			
			EmptyElementsRemoval removal = (EmptyElementsRemoval) context.get("removal");
			removal.add(newArray, setter, origin);

			new Mirror().on(origin).invoke().method(setter).withArgs(newArray);
		}
		array = newArray;
	}
	super.setProperty(context, array, key, value);
	}

	private static Object copyOf(Object array, int desiredLength, int currentLength) {
	Object newArray = Array.newInstance(array.getClass().getComponentType(), desiredLength + 1);
	for (int i = 0; i < currentLength; i++) {
		Array.set(newArray, i, Array.get(array, i));
	}
	for (int i = currentLength; i < desiredLength; i++) {
		Array.set(newArray, i, null);
	}
	array = newArray;
	return array;
	}

}
